package com.zhj.helloframework.aop;

import com.zhj.helloframework.aop.annotation.Aspect;
import com.zhj.helloframework.aop.annotation.Order;
import com.zhj.helloframework.aop.aspect.AspectInfo;
import com.zhj.helloframework.aop.aspect.DefaultAspect;
import com.zhj.helloframework.core.BeanContainer;
import com.zhj.helloframework.util.ValidationUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @Author: zhj
 * @Description: 从IOC容器中取出我们想要实现增强逻辑的类，然后对其进行增强操作之后，再将其放回到IOC容器中
 * @Date: 2021/10/8 16:57
 * @Version: 1.0
 */
public class AspectWeaver {

    private BeanContainer beanContainer;

    // 获取BeanContainer对象，以便在进行AOP操作时，可以获得切面类以及增强类
    public AspectWeaver() {
        beanContainer = BeanContainer.getInstance();
    }

    // 进行AOP增强操作
    public void doAop() {
        // 1.根据@Aspect注解获取所有的切面类
        Set<Class<?>> aspectSet = beanContainer.getClassesByAnnotation(Aspect.class);
        if (ValidationUtil.isEmpty(aspectSet)) {
            return;
        }
        // 2. 拼装AspectInfoList：将切面类转换成AspectInfoList列表，方便后续使用
        List<AspectInfo> aspectInfoList = packAspectInfoList(aspectSet);
        // 3. 遍历容器里的类
        Set<Class<?>> classSet = beanContainer.getClasses();// 获取容器中所有的Class对象
        for (Class<?> targetClass : classSet) {// 遍历
            // 排除AspectClass自身，因为不需要对切面类进行增强
            if (targetClass.isAnnotationPresent(Aspect.class)) {
                continue;
            }
            // 4. 粗筛符和条件的Aspect
            List<AspectInfo> roughMatchedAspectList  = collectRoughMatchAspectListForSpecificClass(aspectInfoList, targetClass);
            // 5. 尝试进行Aspect织入（尝试对targetClass进行增强）
            wrapIfNecessary(roughMatchedAspectList,targetClass);
        }
    }

    /**
     * 将切面类对象集合转换为AspectInfoList列表
     * @param aspectSet 切面类对象集合
     * @return AspectInfoList列表
     */
    private List<AspectInfo> packAspectInfoList(Set<Class<?>> aspectSet) {
        List<AspectInfo> aspectInfoList = new ArrayList<>();
        for (Class<?> aspectClass : aspectSet) {
            // 进行切面类校验：切面类必须实现DefaultAspect抽象类，必须有@Order和@Aspect注解
            if (verifyAspect(aspectClass)) {
                Order orderTag = aspectClass.getAnnotation(Order.class);
                Aspect aspectTag = aspectClass.getAnnotation(Aspect.class);
                // 获取切面类对象
                DefaultAspect defaultAspect = (DefaultAspect) beanContainer.getBean(aspectClass);
                // 初始化表达式定位器
                PointcutLocator pointcutLocator = new PointcutLocator(aspectTag.pointcut());
                // 组合成AspectInfo对象
                AspectInfo aspectInfo = new AspectInfo(orderTag.value(),defaultAspect,pointcutLocator);
                aspectInfoList.add(aspectInfo);
            } else {
                throw new RuntimeException("@Aspect and @Order must be added to the Aspect class, and Aspect class must extend from DefaultAspect");
            }
        }
        return aspectInfoList;
    }

    /**
     * 切面类校验
     * @param aspectClass 切面类
     * @return boolean
     */
    private boolean verifyAspect(Class<?> aspectClass) {
        // 框架中一定要遵守给Aspect添加的@Aspect和@Order标签的规范，同时，必须继承自DefaultAspect.class
        return aspectClass.isAnnotationPresent(Aspect.class)
                && aspectClass.isAnnotationPresent(Order.class)
                && DefaultAspect.class.isAssignableFrom(aspectClass);
    }

    /**
     * 粗筛符和条件的Aspect
     * @param aspectInfoList 切面类列表
     * @param targetClass 指定增强的目标类
     * @return
     */
    private List<AspectInfo> collectRoughMatchAspectListForSpecificClass(List<AspectInfo> aspectInfoList,Class<?> targetClass) {
        List<AspectInfo> roughMatchedAspectList = new ArrayList<>();
        for (AspectInfo aspectInfo : aspectInfoList) {
            // 粗筛
            if (aspectInfo.getPointcutLocator().roughMatches(targetClass)) {
                roughMatchedAspectList.add(aspectInfo);
            }
        }
        return roughMatchedAspectList;
    }

    /**
     * 利用初筛后的切面类列表和指定增强的目标类生成增强后的类，并放入IOC容器中
     * @param roughMatchedAspectList 初筛后的切面类列表
     * @param targetClass 指定增强的目标类
     */
    private void wrapIfNecessary(List<AspectInfo> roughMatchedAspectList,Class<?> targetClass) {
        if (ValidationUtil.isEmpty(roughMatchedAspectList)) {
            return;
        }
        // 创建动态代理对象
        AspectListExecutor aspectListExecutor = new AspectListExecutor(targetClass,roughMatchedAspectList);
        Object proxyBean = ProxyCreator.createProxy(targetClass, aspectListExecutor);
        beanContainer.addBean(targetClass,proxyBean);
    }
}
